/**
 * \file XMLNode.cpp
 * \brief XMLNode definition class
 * \author Edouard ROGE
 * \date 16 October 2012
 */

#include <algorithm>
#include "Core/Log/LogManager.h"
#include "XMLElement.h"
#include "XMLTextNode.h"
#include "XMLNode.h"

namespace Core
{
	namespace File
	{
		XMLNode::XMLNode()
		{

		}

		XMLNode::~XMLNode()
		{
			for (uint32 i = 0; i < m_Children.size(); ++i)
				delete m_Children[i];

		}

		void XMLNode::SetName(const std::string & _strName)
		{
			m_strName = _strName;
		}

		const std::string & XMLNode::GetName() const
		{
			return m_strName;
		}

		const std::vector<XMLNode *> & XMLNode::GetChildren() const
		{
			return m_Children;
		}

		XMLNode * XMLNode::GetChild(uint32 _iIndex) const
		{
			return (_iIndex < m_Children.size() ? m_Children[_iIndex] : NULL);
		}

		void XMLNode::SetChildren(const std::vector<XMLNode *> & _nodeChildren)
		{
			m_Children = _nodeChildren;
		}

		void XMLNode::AddChild(XMLNode * _nodeChild)
		{
			m_Children.push_back(_nodeChild);
		}

		error XMLNode::GetNameAndAttributes(const std::string & _strLine, std::vector<XMLAttribute*> & _attributes, std::string & _strName)
		{
			// Node name
			int32 iBegin = _strLine.find_first_of('<');
			if (iBegin < 0) return errFailed;

			int32 iEnd = _strLine.find_first_of(' ', iBegin + 1);
			if (iEnd < 0) iEnd = _strLine.find_first_of('>', iBegin + 1);
			if (iEnd < 0) return errFailed;

			_strName = _strLine.substr(iBegin + 1, iEnd - 1 - iBegin);

			// Attributes
			int32 iBeginAttributeName = iEnd + 1;
			int32 iEndAttributeName;
			while ((iEndAttributeName = _strLine.find_first_of('=', iBeginAttributeName)) != -1)
			{
				// error in XML
				if (iBeginAttributeName >= iEndAttributeName) return errFailed;

				std::string attributeName = _strLine.substr(iBeginAttributeName, iEndAttributeName - iBeginAttributeName);
				attributeName.erase(std::remove(std::begin(attributeName), std::end(attributeName), ' '), std::end(attributeName));

				int32 iBeginAttributeValue = _strLine.find_first_of('\"', iEndAttributeName);

				// error in XML
				if (iBeginAttributeValue < 0) return errFailed;

				int32 iEndAttributeValue = _strLine.find_first_of('\"', iBeginAttributeValue + 1);

				// error in XML
				if (iEndAttributeValue < 0 || iEndAttributeValue - iBeginAttributeValue <= 1) return errFailed;

				std::string attributeValue = _strLine.substr(iBeginAttributeValue + 1, iEndAttributeValue - iBeginAttributeValue - 1);

				_attributes.push_back(new XMLAttribute(attributeName, attributeValue));

				iBeginAttributeName = iEndAttributeValue + 1;
			}
			return errOK;
		}

		XMLNode * XMLNode::Load(File & _file)
		{
			XMLNode * pNode = NULL;
			std::string str;
			char c;
			_file >> c;

			// look for the first '<' char
			while (c != '<' && c != File::EndOfFile) _file >> c;
			if (c == File::EndOfFile) return NULL;

			str += c;
			// store all character until the first '>' which is not between two quotes
			bool betweenQuotes = false;
			while (c != '>' && c != File::EndOfFile)
			{
				if (c == '\"')
					betweenQuotes = !betweenQuotes;

				// go on
				_file >> c;
				// store in a string
				str += c;
			}

			if (c == File::EndOfFile)
			{
				LOG_ERROR("Error while reading xml file, expecting \'>\'");
				return NULL;
			}
			if (betweenQuotes)
			{
				LOG_ERROR("Error while reading xml file, expecting \'\"\'");
				return NULL;
			}

			std::vector<XMLAttribute*> attributes;
			std::string strName;
			if (XMLNode::GetNameAndAttributes(str, attributes, strName) == errOK)
			{
				if (attributes.size() == 0)
				{
					pNode = new XMLTextNode();
				}
				else
				{
					pNode = new XMLElement();
					dynamic_cast<XMLElement*>(pNode)->SetAttributes(attributes);
				}
				pNode->SetName(strName);
			}
			else // error while reading xml file
			{
				for (uint32 i = 0; i<attributes.size(); ++i)
					delete attributes[i];
				attributes.clear();
				LOG_ERROR("Error while reading element %s", strName.c_str());
				return NULL;
			}

			if (str.length()>2 && str[str.length() - 2] == '/')
			{
				return pNode;
			}
			else
			{
				XMLNode * pChild = XMLNode::Load(_file);
				while (pChild != NULL)
				{
					std::string endBalise = "/";
					endBalise += strName;
					if (pChild->GetName() == endBalise)
					{
						delete pChild;
						pChild = NULL;
					}
					else
					{
						pNode->m_Children.push_back(pChild);
						pChild = XMLNode::Load(_file);
					}
				}
			}

			return pNode;
		}
	}
}